<script lang="tsx">
    import type {PropType, Ref} from 'vue';
    import type {FormActionType, FormProps} from '../types/form';
    import type {FormSchema} from '../types/form';
    import type {ValidationRule} from 'ant-design-vue/lib/form/Form';
    import type {TableActionType} from '/@/components/Table';
    import {defineComponent, computed, unref, toRefs} from 'vue';
    import {Form, Col, Divider} from 'ant-design-vue';
    import {componentMap} from '../componentMap';
    import {BasicHelp} from '/@/components/Basic';
    import {isBoolean, isFunction, isNull} from '/@/utils/is';
    import {getSlot} from '/@/utils/helper/tsxHelper';
    import {createPlaceholderMessage, setComponentRuleType} from '../helper';
    import {upperFirst, cloneDeep} from 'lodash-es';
    import {useItemLabelWidth} from '../hooks/useLabelWidth';
    import {useI18n} from '/@/hooks/web/useI18n';

    export default defineComponent({
        name: 'BasicFormItem',
        inheritAttrs: false,
        props: {
            schema: {
                type: Object as PropType<FormSchema>,
                default: () => ({}),
            },
            formProps: {
                type: Object as PropType<FormProps>,
                default: () => ({}),
            },
            allDefaultValues: {
                type: Object as PropType<Recordable>,
                default: () => ({}),
            },
            formModel: {
                type: Object as PropType<Recordable>,
                default: () => ({}),
            },
            setFormModel: {
                type: Function as PropType<(key: string, value: any) => void>,
                default: null,
            },
            tableAction: {
                type: Object as PropType<TableActionType>,
            },
            formActionType: {
                type: Object as PropType<FormActionType>,
            },
        },
        setup(props, {slots}) {
            const {t} = useI18n();

            const {schema, formProps} = toRefs(props) as {
                schema: Ref<FormSchema>;
                formProps: Ref<FormProps>;
            };

            const itemLabelWidthProp = useItemLabelWidth(schema, formProps);

            const getValues = computed(() => {
                const {allDefaultValues, formModel, schema} = props;
                const {mergeDynamicData} = props.formProps;
                return {
                    field: schema.field,
                    model: formModel,
                    values: {
                        ...mergeDynamicData,
                        ...allDefaultValues,
                        ...formModel,
                    } as Recordable,
                    schema: schema,
                };
            });

            const getComponentsProps = computed(() => {
                const {schema, tableAction, formModel, formActionType} = props;
                let {componentProps = {}} = schema;
                if (isFunction(componentProps)) {
                    componentProps = componentProps({schema, tableAction, formModel, formActionType}) ?? {};
                }
                if (schema.component === 'Divider') {
                    componentProps = Object.assign({type: 'horizontal'}, componentProps, {
                        orientation: 'left',
                        plain: true,
                    });
                }
                return componentProps as Recordable;
            });

            const getDisable = computed(() => {
                const {disabled: globDisabled} = props.formProps;
                const {dynamicDisabled} = props.schema;
                const {disabled: itemDisabled = false} = unref(getComponentsProps);
                let disabled = !!globDisabled || itemDisabled;
                if (isBoolean(dynamicDisabled)) {
                    disabled = dynamicDisabled;
                }
                if (isFunction(dynamicDisabled)) {
                    disabled = dynamicDisabled(unref(getValues));
                }
                return disabled;
            });

            function getShow(): { isShow: boolean; isIfShow: boolean } {
                const {show, ifShow} = props.schema;
                const {showAdvancedButton} = props.formProps;
                const itemIsAdvanced = showAdvancedButton
                    ? isBoolean(props.schema.isAdvanced)
                        ? props.schema.isAdvanced
                        : true
                    : true;

                let isShow = true;
                let isIfShow = true;

                if (isBoolean(show)) {
                    isShow = show;
                }
                if (isBoolean(ifShow)) {
                    isIfShow = ifShow;
                }
                if (isFunction(show)) {
                    isShow = show(unref(getValues));
                }
                if (isFunction(ifShow)) {
                    isIfShow = ifShow(unref(getValues));
                }
                isShow = isShow && itemIsAdvanced;
                return {isShow, isIfShow};
            }

            function handleRules(): ValidationRule[] {
                const {
                    rules: defRules = [],
                    component,
                    rulesMessageJoinLabel,
                    label,
                    dynamicRules,
                    required,
                } = props.schema;

                if (isFunction(dynamicRules)) {
                    return dynamicRules(unref(getValues)) as ValidationRule[];
                }

                let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
                const {rulesMessageJoinLabel: globalRulesMessageJoinLabel} = props.formProps;

                const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
                    ? rulesMessageJoinLabel
                    : globalRulesMessageJoinLabel;
                const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;

                function validator(rule: any, value: any) {
                    const msg = rule.message || defaultMsg;
                    if (value === undefined || isNull(value)) {
                        // 空值
                        return Promise.reject(msg);
                    } else if (Array.isArray(value) && value.length === 0) {
                        // 数组类型
                        return Promise.reject(msg);
                    } else if (typeof value === 'string' && value.trim() === '') {
                        // 空字符串
                        return Promise.reject(msg);
                    } else if (
                        typeof value === 'object' &&
                        Reflect.has(value, 'checked') &&
                        Reflect.has(value, 'halfChecked') &&
                        Array.isArray(value.checked) &&
                        Array.isArray(value.halfChecked) &&
                        value.checked.length === 0 &&
                        value.halfChecked.length === 0
                    ) {
                        // 非关联选择的tree组件
                        return Promise.reject(msg);
                    }
                    return Promise.resolve();
                }

                const getRequired = isFunction(required) ? required(unref(getValues)) : required;

                if ((!rules || rules.length === 0) && getRequired) {
                    rules = [{required: getRequired, validator}];
                }

                const requiredRuleIndex: number = rules.findIndex(
                    (rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator')
                );

                if (requiredRuleIndex !== -1) {
                    const rule = rules[requiredRuleIndex];
                    const {isShow} = getShow();
                    if (!isShow) {
                        rule.required = false;
                    }
                    if (component) {
                        if (!Reflect.has(rule, 'type')) {
                            rule.type = component === 'InputNumber' ? 'number' : 'string';
                        }

                        rule.message = rule.message || defaultMsg;

                        if (component.includes('Input') || component.includes('Textarea')) {
                            rule.whitespace = true;
                        }
                        const valueFormat = unref(getComponentsProps)?.valueFormat;
                        setComponentRuleType(rule, component, valueFormat);
                    }
                }

                // Maximum input length rule check
                const characterInx = rules.findIndex((val) => val.max);
                if (characterInx !== -1 && !rules[characterInx].validator) {
                    rules[characterInx].message =
                        rules[characterInx].message ||
                        t('component.form.maxTip', [rules[characterInx].max] as Recordable);
                }
                return rules;
            }

            function renderComponent() {
                const {
                    renderComponentContent,
                    component,
                    field,
                    changeEvent = 'change',
                    valueField,
                } = props.schema;

                const isCheck = component && ['Switch', 'Checkbox'].includes(component);

                const eventKey = `on${upperFirst(changeEvent)}`;

                const on = {
                    [eventKey]: (...args: Nullable<Recordable>[]) => {
                        const [e] = args;
                        if (propsData[eventKey]) {
                            propsData[eventKey](...args);
                        }
                        const target = e ? e.target : null;
                        const value = target ? (isCheck ? target.checked : target.value) : e;
                        props.setFormModel(field, value);
                    },
                };
                const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

                const {autoSetPlaceHolder, size} = props.formProps;
                const propsData: Recordable = {
                    allowClear: true,
                    getPopupContainer: (trigger: Element) => trigger.parentNode,
                    size,
                    ...unref(getComponentsProps),
                    disabled: unref(getDisable),
                };

        const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
        // RangePicker place是一个数组
        if (isCreatePlaceholder && component !== 'RangePicker' && component) {
          //自动设置placeholder
          propsData.placeholder =
            unref(getComponentsProps)?.placeholder ||
            createPlaceholderMessage(component) + props.schema.label;
        }
        propsData.codeField = field;
        propsData.formValues = unref(getValues);

                const bindValue: Recordable = {
                    [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
                };

                const compAttr: Recordable = {
                    ...propsData,
                    ...on,
                    ...bindValue,
                };

                if (!renderComponentContent) {
                    return <Comp {...compAttr} />;
                }
                const compSlot = isFunction(renderComponentContent)
                    ? {...renderComponentContent(unref(getValues))}
                    : {
                        default: () => renderComponentContent,
                    };
                return <Comp {...compAttr}>{compSlot}</Comp>;
            }

            /**
             *渲染Label
             * @updateBy:zyf
             */
            function renderLabelHelpMessage() {
                const {label, helpMessage, helpComponentProps, subLabel} = props.schema;
                const renderLabel = subLabel ? (
                    <span>
            {label} <span class="text-secondary">{subLabel}</span>
          </span>
                ) : (
                    label
                );
                const getHelpMessage = isFunction(helpMessage)
                    ? helpMessage(unref(getValues))
                    : helpMessage;
                if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
                    return renderLabel;
                }
                return (
                    <span>
            {renderLabel}
                        <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
          </span>
                );
            }

            function renderItem() {
                const {itemProps, slot, render, field, suffix, component} = props.schema;
                const {labelCol, wrapperCol} = unref(itemLabelWidthProp);
                const {colon} = props.formProps;

                if (component === 'Divider') {
                    return (
                        <Col span={24}>
                            <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
                        </Col>
                    );
                } else {
                    const getContent = () => {
                        return slot
                            ? getSlot(slots, slot, unref(getValues))
                            : render
                                ? render(unref(getValues))
                                : renderComponent();
                    };

                    const showSuffix = !!suffix;
                    const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;

                    return (
                        <Form.Item
                            name={field}
                            colon={colon}
                            class={{'suffix-item': showSuffix}}
                            {...(itemProps as Recordable)}
                            label={renderLabelHelpMessage()}
                            rules={handleRules()}
                            labelCol={labelCol}
                            wrapperCol={wrapperCol}
                        >
                            <div style="display:flex">
                                <div style="flex:1;">{getContent()}</div>
                                {showSuffix && <span class="suffix">{getSuffix}</span>}
                            </div>
                        </Form.Item>
                    );
                }
            }

            return () => {
                const {colProps = {}, colSlot, renderColContent, component} = props.schema;
                if (!componentMap.has(component)) {
                    return null;
                }

                const {baseColProps = {}} = props.formProps;
                const realColProps = {...baseColProps, ...colProps};
                const {isIfShow, isShow} = getShow();
                const values = unref(getValues);

                const getContent = () => {
                    return colSlot
                        ? getSlot(slots, colSlot, values)
                        : renderColContent
                            ? renderColContent(values)
                            : renderItem();
                };

                return (
                    isIfShow && (
                        <Col {...realColProps} v-show={isShow}>
                            {getContent()}
                        </Col>
                    )
                );
            };
        },
    });
</script>
